package com.dd.ent.car.ui.widget.webview;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.webkit.JavascriptInterface;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.dd.ent.car.AppContext;
import com.dd.ent.car.common.http.VolleyManager;
import com.pp.ent.base.log.MLog;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

/**
 * Author:LiuJianSheng
 * Date:2015/12/19
 */
public abstract class BaseJsInterface {
    public static final String JS_PREFIX = "javascript:";
    protected static final String TAG = "JsInterface";
    public static final String NAME = "XLJSWebViewBridge";
    private static final int WHAT_SEND_MESSAGE = 1;
    private static final String EXTRA_METHOD_NAME = "MethodName";
    private static final String EXTRA_PARAMS = "Params";
    private static final String EXTRA_CALLBACK = "Callback";

    private static RequestQueue sQueue = VolleyManager.instance(AppContext.getInstance()).getQueue();
    private CustomWebView mWebView;
    private Context mContext;
    private Handler mHandler = new JsHandler(Looper.getMainLooper());

    public BaseJsInterface() {

    }

    public BaseJsInterface(Context context, CustomWebView webView) {
        setupParams(context, webView);
    }

    public void setupParams(Context context, CustomWebView webView) {
        this.mWebView = webView;
        this.mContext = context;
    }

    /**
     * 提供给网页调用的Js方法
     *
     * @param methodNameStr 本地对应的方法名称
     * @param params        参数列表，json格式
     * @param callback      回调函数
     */
    @JavascriptInterface
    public void sendMessage(String methodNameStr, String params, String callback) {
        if (TextUtils.isEmpty(methodNameStr)) {
            MLog.error(TAG, "method Name is Empty");
            return;
        }
        MLog.debug(TAG, "sendMessage--methodNameStr=" + methodNameStr + "|params=" + params + "|callback=" + callback);
        Message message = Message.obtain();
        message.what = WHAT_SEND_MESSAGE;
        Bundle data = new Bundle();
        data.putString(EXTRA_METHOD_NAME, methodNameStr);
        data.putString(EXTRA_CALLBACK, callback);
        data.putString(EXTRA_PARAMS, params);
        message.setData(data);
        mHandler.sendMessage(message);
    }

    protected Context getContext() {
        return mContext;
    }

    protected CustomWebView getWebView() {
        return mWebView;
    }

    protected abstract boolean handleMessage(MethodName methodName, JSONObject params, String callback) throws JSONException;

    /**
     * 只有一个参数的回调，可以直接调用该方法
     *
     * @param callback
     * @param key
     * @param value
     */
    protected void callbackWithOneParam(String callback, String key, Object value) {
        Map<String, Object> params = null;
        if (!TextUtils.isEmpty(key)) {
            params = new HashMap<>();
            params.put(key, value);
        }
        callback(callback, params);
    }

    protected void callbackError(String callback, int errorCode, String errorMsg) {
        Map<String, Object> internalErrorParams = new HashMap<>();
        internalErrorParams.put("errorCode", errorCode);
        internalErrorParams.put("errorMsg", errorMsg);
        Map<String, Object> params = new HashMap<>();
        params.put("internalError", internalErrorParams);
        callback(callback, params);
    }

    protected void callback(final String callback, final Map<String, Object> params) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                callbackDirect(callback, params);
            }
        });
    }

    protected void callbackDirect(String callback, Map<String, Object> params) {
        if (TextUtils.isEmpty(callback)) {
            return;
        }
        String urlCallback = generateCallbackJson(callback, params);
        if (!TextUtils.isEmpty(urlCallback)) {
            mWebView.loadUrl(urlCallback);
        } else {
            MLog.error(TAG, "callbackDirect - CallbackUrl is null : " + callback);
        }
    }

    protected void add(Request request) {
        sQueue.add(request);
    }

    private String generateCallbackJson(String callback, Map<String, Object> params) {
        String urlCallback;
        if (params == null || params.isEmpty()) {
            urlCallback = mergeJsCallbackUrl(callback);
        } else {
            String param = new JSONObject(params).toString();
            urlCallback = mergeJsCallbackUrl(callback, param);
        }
        return urlCallback;
    }

    /**
     * 合并方法名和参数列表成一个完整的可用于JS调用的方法
     *
     * @param methodName 方法名
     * @param params     参数列表
     * @return
     */
    private static String mergeJsCallbackUrl(String methodName, String... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(JS_PREFIX);
        sb.append(methodName);
        sb.append('(');
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                sb.append("'");
                if (params[i] != null) {
                    sb.append(params[i].replace("\\", "\\\\").replace("'", "\\'"));
                }
                sb.append("'");
                sb.append(',');
            }
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(')');
        return sb.toString();
    }

    protected void runOnUiThread(Runnable runnable) {
        mWebView.post(runnable);
    }

    private class JsHandler extends Handler {
        public JsHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case WHAT_SEND_MESSAGE:
                    Bundle data = msg.peekData();
                    if (data != null) {
                        String methodNameStr = data.getString(EXTRA_METHOD_NAME);
                        String params = data.getString(EXTRA_PARAMS);
                        String callback = data.getString(EXTRA_CALLBACK);
                        MethodName methodName = MethodName.getMethod(methodNameStr);
                        if (methodName == null) {
                            MLog.error(TAG, "methodName : " + methodNameStr + " should declared in enum MethodName");
                            if (!TextUtils.isEmpty(callback)) {
                                callbackError(callback, 1, "method " + methodNameStr + " is not declared");
                            }
                            return;
                        }
                        try {
                            if (isEmpty(params)) {
                                BaseJsInterface.this.handleMessage(methodName, null, callback);
                                return;
                            }
                            JSONObject paramsJson = new JSONObject(params);
                            BaseJsInterface.this.handleMessage(methodName, paramsJson, callback);
                        } catch (JSONException e) {
                            MLog.error(TAG, e);
                            if (!isEmpty(callback)) {
                                callbackError(callback, 1, "Params is Error");
                            }
                        }
                    }
                    break;
            }
        }
    }

    private static boolean isEmpty(String string) {
        if (TextUtils.isEmpty(string) || "null".equalsIgnoreCase(string)) {
            return true;
        }
        return false;
    }
}
